///////////////////////////////////////////////////////////////////////////////
//
/// Copyright (c) 2022- ThunderSoft
/// All Rights Reserved by Thunder Software Technology Co., Ltd and its affiliates.
/// You may not use, copy, distribute, modify, transmit in any form this file
/// except in compliance with ThunderSoft in writing by applicable law.
//
///////////////////////////////////////////////////////////////////////////////

#ifndef _DRA_COM_SOMEIP_SERIALIZER_H_
#define _DRA_COM_SOMEIP_SERIALIZER_H_

#include <mutex>
// #include "dra/core/typedefine.h"
#include "serialize/typedefine.h"
#include "serialize/ISerializer.h"
// #include "dra/core/defines.h"
#include "serialize/defines.h"
#include "serialize/serializeCommon.h"

#define GOS_LOG_ERR(...) \
    if (false) {fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n");}
#define GOS_LOG_INFO(...) \
    if (false) {fprintf(stdout, __VA_ARGS__), fprintf(stdout, "\n");}

namespace dra {
namespace com {
// namespace someip {

// using namespace deviceware::base;

#define SINGLE_BUFFER_SIZE  1024
// #define SINGLE_BUFFER_SIZE  100
#define BUFFER_NUMBER  1
#define INIT_BUFFER_SIZE  (SINGLE_BUFFER_SIZE*BUFFER_NUMBER)
#define BUFFER_E2E_TAIL 4


class DRA_EXPORT Serializer : public dra::com::ISerializer {
public:
    // virtual BOOLEAN setByte(BYTE b);
    virtual BOOLEAN setBoolean(BOOLEAN b){return write(b);}
    virtual BOOLEAN setInt8(INT8 i){return write(i);}
    virtual BOOLEAN setInt16(INT16 i){return write(i);}
    virtual BOOLEAN setInt32(INT32 i){return write(i);}
    virtual BOOLEAN setInt64(INT64 i){return write(i);}
    virtual BOOLEAN setUInt8(UINT8 i){return write(i);}
    virtual BOOLEAN setUInt16(UINT16 i){return write(i);}
    virtual BOOLEAN setUInt32(UINT32 i){return write(i);}
    virtual BOOLEAN setUInt64(UINT64 i){return write(i);}
    virtual BOOLEAN setFloat32(FLOAT32 f){return write(f);}
    virtual BOOLEAN setFloat64(FLOAT64 f){return write(f);}
    virtual BOOLEAN setString([[maybe_unused]] const CHAR* str, [[maybe_unused]] INT32 strLen){return false;}
    // Begin Only For ETS Test------------------------------------------------------
    virtual BOOLEAN setString([[maybe_unused]] CHAR const* str, [[maybe_unused]] INT32 strLen, 
            [[maybe_unused]] UINT32 flLen, [[maybe_unused]] BaseTypeEncodingString encodeType){return false;}
    // End   Only For ETS Test------------------------------------------------------
    virtual BOOLEAN setFD([[maybe_unused]] INT32 fd){return false;}
    virtual dra::com::SERIALIZE_TYPE GetSerializeType() {
        return dra::com::SERIALIZE_TYPE::SOMEIP;
    }

    CHAR* getSerialContent(INT32& sdataLen);
#ifdef HAS_E2EXF
    CHAR* getSerialContentE2E(UINT32 &dataid, INT32& sdataLen);
    VOID addE2EResultTail(CHAR * data, INT32 len);
#endif  // HAS_E2EXF

    template < typename ArgumentType_ >
    BOOLEAN write(const ArgumentType_ value) {
        write((uint8_t*)&value, sizeof(ArgumentType_));
        return true;
    }

    BOOLEAN write(const uint8_t* _data, int len) {
        payload_.insert(payload_.end(), _data, _data + len);
        return true;
    }

    std::vector<uint8_t> getSerialContent() {
        return std::move(payload_);
    }

    Serializer(): m_pBuf(nullptr),
                m_iOffset(0),
                m_bIsFull(false),
                m_iBufItemNum(0){;}
    virtual ~Serializer(){;}

private:
    template<typename T>
    BOOLEAN setBasicType(T const & t, INT32 iType);
    BOOLEAN reBufSize(INT32 iWantSize);  // double add

    // for string
    BOOLEAN setBOMTermination(UINT8* bom, INT32& bomLen, INT32& terLen);
    BOOLEAN convert2UTF16(CHAR* inbuf, size_t inbytesleft, CHAR* outbuf, size_t& outbytesleft);
    INT32 getSizeOfStringLengthField(void);
    // Begin Only For ETS Test-------------------------------------------------------
    BOOLEAN setBOMTermination(UINT8* bom, INT32& bomLen, INT32& terLen, BaseTypeEncodingString encodeType);
    BOOLEAN convert2UTF16(CHAR* inbuf, size_t inbytesleft, CHAR* outbuf, size_t& outbytesleft,
                BaseTypeEncodingString encodeType);
    INT32 getSizeOfStringLengthField(UINT32 flLen);
    // End   Only For ETS Test-------------------------------------------------------

private:
    CHAR *m_pBuf;
    INT32 m_iOffset;
    BOOLEAN m_bIsFull;
    INT32 m_iBufItemNum;
    std::mutex m_mtx_syn;

    std::vector<uint8_t> payload_;
};

// length field serialization -----------------------------------------------------------------------------------------
// Begin Only For ETS Test---------------------------------------------------------------------------------------------
inline UINT32 GetSerializerLengthFieldLength(UINT32 len) {
    UINT32 lf_len = len;
    if ((len != 1) && (len != 2) && (len != 4)) {
        lf_len = 0;
    }
    return lf_len;
}
// End Only For ETS Test---------------------------------------------------------------------------------------------

BOOLEAN serialize_lf(ISerializer *s, UINT32 sizeOfLengthField, UINT32 length);
// C array serialization
template <typename T>
BOOLEAN serialize_carray(ISerializer *s, T* a, size_t size);

template <typename T, typename Enalbe = void>
struct serialize_wrapper {
    static BOOLEAN
    set(ISerializer *s, T const &t,
        std::enable_if_t<std::is_same<BOOLEAN, decltype(t.serialize(s))>::value> * = nullptr) {
        GOS_LOG_INFO("serialize_wrapper set line:%d\n", __LINE__);
        return t.serialize(s);
    }
};

template <typename T>
struct serialize_wrapper <T, typename std::enable_if_t<std::is_enum<T>::value>> {
    static BOOLEAN set(ISerializer* s, T const& t) {
        GOS_LOG_INFO("serialize_wrapper set line:%d\n", __LINE__);
        return serialize_wrapper<std::underlying_type_t<T>>::set(s, static_cast<std::underlying_type_t<T>>(t));
    }
};

template <typename T>
struct serialize_wrapper <T, typename std::enable_if_t<IsStruct<T>::value>> {
    static BOOLEAN set(ISerializer* s, T const& t) {
        GOS_LOG_INFO("serialize_wrapper set line:%d\n", __LINE__);
        return serialize_lf(s, transProps.sizeOfStructLengthField, t.GetLength(dra::com::SERIALIZE_TYPE::SOMEIP))
            && t.serialize(s);
    }
};

template <typename T>
struct serialize_wrapper <T, typename std::enable_if_t<IsUnion<T>::value>> {
    static BOOLEAN set(ISerializer* s, T const& t) {
        GOS_LOG_INFO("serialize_wrapper set line:%d\n", __LINE__);
        return serialize_lf(s, transProps.sizeOfUnionLengthField, t.GetLength(dra::com::SERIALIZE_TYPE::SOMEIP))
            && t.serialize(s);
    }
};

template <typename T>
struct serialize_wrapper<T*> {
    static BOOLEAN set(ISerializer* s, T* const& t,
            std::enable_if_t<std::is_same<BOOLEAN, decltype(t->serialize(s))>::value>* = nullptr) {
        GOS_LOG_INFO("serialize_wrapper set line:%d\n", __LINE__);
        return t->serialize(s);
    }
};

template <>
struct serialize_wrapper<BOOLEAN> {
    static BOOLEAN set(ISerializer *s, BOOLEAN const &b) {
        return s->setBoolean(b);
    }
};

template <>
struct serialize_wrapper<FLOAT32> {
    static BOOLEAN set(ISerializer *s, FLOAT32 const &f) {
        return s->setFloat32(f);
    }
};

template <>
struct serialize_wrapper<FLOAT64> {
    static BOOLEAN set(ISerializer *s, FLOAT64 const &f) {
        return s->setFloat64(f);
    }
};

template <>
struct serialize_wrapper<INT32> {
    static BOOLEAN set(ISerializer *s, INT32 const &i) {
        GOS_LOG_INFO("serialize_wrapper set line:%d\n", __LINE__);
        return s->setInt32(i);
    }
};

template <>
struct serialize_wrapper<INT8> {
    static BOOLEAN set(ISerializer *s, INT8 const &i) {
        GOS_LOG_INFO("serialize_wrapper set line:%d\n", __LINE__);
        return s->setInt8(i);
    }
};

template <>
struct serialize_wrapper<char> {
    static BOOLEAN set(ISerializer *s, char const &i) {
        GOS_LOG_INFO("serialize_wrapper set line:%d\n", __LINE__);
        return s->setInt8(static_cast<INT8>(i));
    }
};

template <>
struct serialize_wrapper<INT16> {
    static BOOLEAN set(ISerializer *s, INT16 const &i) {
        GOS_LOG_INFO("serialize_wrapper set line:%d\n", __LINE__);
        return s->setInt16(i);
    }
};

template <>
struct serialize_wrapper<INT64> {
    static BOOLEAN set(ISerializer *s, INT64 const &i) {
        GOS_LOG_INFO("serialize_wrapper set line:%d\n", __LINE__);
        return s->setInt64(i);
    }
};

#if !defined(__aarch64_oe__) && !defined(__aarch32_oe__) && !defined(__ANDROID__)
template <>
struct serialize_wrapper<int64_t> {
    static BOOLEAN set(ISerializer *s, int64_t const &i) {
        GOS_LOG_INFO("serialize_wrapper set line:%d\n", __LINE__);
        return s->setInt64(static_cast<INT64>(i));
    }
};
#endif

template <>
struct serialize_wrapper<UINT32> {
    static BOOLEAN set(ISerializer *s, UINT32 const &i) {
        GOS_LOG_INFO("serialize_wrapper set line:%d\n", __LINE__);
        return s->setUInt32(i);
    }
};

template <>
struct serialize_wrapper<wchar_t> {
    static BOOLEAN set(ISerializer *s, wchar_t const &i) {
        return s->setUInt32(static_cast<UINT32>(i));
    }
};

template <>
struct serialize_wrapper<UINT8> {
    static BOOLEAN set(ISerializer *s, UINT8 const &i) {
        GOS_LOG_INFO("serialize_wrapper set line:%d\n", __LINE__);
        return s->setUInt8(i);
    }
};

template <>
struct serialize_wrapper<UINT16> {
    static BOOLEAN set(ISerializer *s, UINT16 const &i) {
        return s->setUInt16(i);
    }
};

template <>
struct serialize_wrapper<UINT64> {
    static BOOLEAN set(ISerializer *s, UINT64 const &i) {
        GOS_LOG_INFO("serialize_wrapper set line:%d\n", __LINE__);
        return s->setUInt64(i);
    }
};

#if !defined(__aarch64_oe__) && !defined(__aarch32_oe__) && !defined(__ANDROID__)
template <>
struct serialize_wrapper<uint64_t> {
    static BOOLEAN set(ISerializer *s, uint64_t const &i) {
        GOS_LOG_INFO("serialize_wrapper set line:%d\n", __LINE__);
        return s->setUInt64(static_cast<UINT64>(i));
    }
};
#endif

template <>
struct serialize_wrapper<STRING> {
    static BOOLEAN set(ISerializer *s, STRING const &str) {
        return s->setString(str.c_str(), str.length());
    }
};

// Begin Only For ETS Test---------------------------------------------------------------------------------------------
template <UINT32 L, BaseTypeEncodingString E>
struct serialize_wrapper<STRINGW<L, E>> {
    static BOOLEAN set(ISerializer* s, STRINGW<L, E> const& str) {
        UINT32 lf_len = GetSerializerLengthFieldLength(L);
        return static_cast<Serializer*>(s)->setString(str.str.c_str(), str.str.length(), lf_len, E);
    }
};
// End   Only For ETS Test---------------------------------------------------------------------------------------------

template <>
struct serialize_wrapper<fd_t> {
    static BOOLEAN set(ISerializer *s, fd_t const &fd) {
        return s->setFD(fd.value);
    }
};

/** \uptrace{SWS_CM_10428} */
#if 0
template <>
struct serialize_wrapper<ara::core::ErrorCode> {
    static BOOLEAN set(ISerializer* s, ara::core::ErrorCode const& err) {
        using union_length_type = UINT32;
        using union_type_type = UINT8;
        using struct_length_type = UINT16;
        using domain_value_type = UINT64;
        using error_code_type = UINT32;
        using support_data_type = UINT32;
        static constexpr struct_length_type struct_length
            = sizeof(domain_value_type) + sizeof(error_code_type) + sizeof(support_data_type);
        static constexpr union_length_type union_length
            = sizeof(struct_length_type) + sizeof(union_type_type) + struct_length;

        union_type_type union_type = 0x01;
        domain_value_type domain_type = err.Domain().Id();
        error_code_type err_code = err.Value();
        support_data_type support_type = err.SupportData();

        return s->setUInt32(union_length) &&
                 s->setUInt8(union_type) &&
                 s->setUInt16(struct_length) &&
                 s->setUInt64(domain_type) &&
                 s->setUInt32(err_code) &&
                 s->setUInt32(support_type);
    }
};
#endif

template <typename F, typename... R>
struct serialize_traveller {
    inline static BOOLEAN serialize(ISerializer* s, F const& f, R const&... r) {
        GOS_LOG_INFO("serialize_traveller serialize line:%d\n", __LINE__);
        return (serialize_traveller<F>::serialize(s, f)
            && serialize_traveller<R...>::serialize(s, r...));
    }
};

template <typename F>
struct serialize_traveller<F> {
    inline static BOOLEAN serialize(ISerializer* s, F const& f) {
        GOS_LOG_INFO("serialize_traveller serialize line:%d\n", __LINE__);
        return serialize_wrapper<F>::set(s, f);
    }
};

// the standard template type, beyond the basic type.
// std::tuple
template <typename... Args>
struct serialize_traveller<std::tuple<Args...>> {
    inline static BOOLEAN serialize(ISerializer* s, std::tuple<Args...> const& t) {
        GOS_LOG_INFO("serialize_traveller serialize line:%d\n", __LINE__);
        using indices_for = std::make_index_sequence<std::tuple_size<std::tuple<Args...>>::value>;
        return _serialize(s, t, indices_for{});
    }

private:
    template <std::size_t... Is>
    inline static BOOLEAN _serialize(ISerializer *s, std::tuple<Args...> const &t,
                                     const std::index_sequence<Is...> &) {
        GOS_LOG_INFO("serial type: std::tuple, change to basic type.\n");
        BOOLEAN ret = false;
        // set tuple size to serial data
        INT32 itupleSize = std::tuple_size<std::tuple<Args...>>::value;
        ret = serialize_traveller<INT32>::serialize(s, itupleSize);
        if (true == ret) {
            ret = serialize_traveller<Args...>::serialize(s, std::get<Is>(t)...);
        }
        return ret;
    }
};

// std::array
template <typename T, std::size_t N>
struct serialize_traveller<std::array<T, N>> {
    inline static BOOLEAN serialize(ISerializer* s, std::array<T, N> const& a) {
        BOOLEAN ret = serialize_lf(s, 0, GetLength(a));
        GOS_LOG_INFO("serial type: std::array, change to basic type.\n");
        if (true == ret) {
            for (auto& iVaule : a) {
                ret = serialize_traveller<T>::serialize(s, iVaule);
                if (false == ret) {
                    break;
                }
            }
        }
        return ret;
    }
};

// Begin Only For ETS Test---------------------------------------------------------------------------------------------
// ARRAYW
template <typename T, std::size_t N, UINT32 L>
struct serialize_traveller<ARRAYW<T, N, L>> {
    inline static BOOLEAN serialize(ISerializer* s, ARRAYW<T, N, L> const& a) {
        UINT32 lf_len = GetSerializerLengthFieldLength(L);
        BOOLEAN ret = serialize_lf(s, lf_len, GetLength(a)-lf_len);
        // GOS_LOG_INFO("serial type: ARRAYW, change to basic type.\n");
        if (true == ret) {
            for (auto& iVaule : a.arr) {
                ret = serialize_traveller<T>::serialize(s, iVaule);
                if (false == ret) {
                    break;
                }
            }
        }
        return ret;
    }
};

// VECTORW
template <typename T, UINT32 L>
struct serialize_traveller<VECTORW<T, L>> {
    inline static BOOLEAN serialize(ISerializer* s, VECTORW<T, L> const& v) {
        UINT32 lf_len = GetSerializerLengthFieldLength(L);
        BOOLEAN ret = true;
#ifdef ETS_067
        if (0 != v.vec.size()) {
            ret = serialize_lf(s, lf_len, GetLength(v.vec)-lf_len);
        }
#else
        ret = serialize_lf(s, lf_len, GetLength(v.vec)-lf_len);
#endif
        // GOS_LOG_INFO("serial type: VECTORW, set vector size: %d.\n", v.size());
        if (true == ret) {
            for (auto& iVaule : v.vec) {
                ret = serialize_traveller<T>::serialize(s, iVaule);
                if (false == ret) {
                    break;
                }
            }
        }
        return ret;
    }
};
// End   Only For ETS Test---------------------------------------------------------------

// std::vector
template <typename T>
struct serialize_traveller<std::vector<T>> {
    inline static BOOLEAN serialize(ISerializer* s, std::vector<T> const& v) {
        BOOLEAN ret = serialize_lf(s, transProps.sizeOfArrayLengthField, GetLength(v)-transProps.sizeOfArrayLengthField);
        GOS_LOG_INFO("serial type: std::vector, set vector size: %lu.\n", v.size());
        if (true == ret) {
            for (auto& iVaule : v) {
                ret = serialize_traveller<T>::serialize(s, iVaule);
                if (false == ret) {
                    break;
                }
            }
        }
        return ret;
    }
};

// std::vector<pair>
template <typename TKEY, typename TVALUE>
struct serialize_traveller<std::vector<std::pair<TKEY, TVALUE>>> {
    inline static BOOLEAN serialize(ISerializer* s, std::vector<std::pair<TKEY, TVALUE>> const& v) {
        BOOLEAN ret = serialize_lf(s, transProps.sizeOfArrayLengthField, v.size()*sizeof(std::pair<TKEY, TVALUE>));
        // GOS_LOG_INFO("serial type: std::vector<pair<,>>, change to basic type.\n");
        if (true == ret) {
            for (auto& iVaule : v) {
                BOOLEAN retKey = serialize_traveller<TKEY>::serialize(s, iVaule.first);
                BOOLEAN retValue = serialize_traveller<TVALUE>::serialize(s, iVaule.second);
                if (false == retKey || false == retValue) {
                    return false;
                }
            }
        }
        return ret;
    }
};

// std::map
// map size, map key, map key value
template <typename TKEY, typename TVALUE>
struct serialize_traveller<std::map<TKEY, TVALUE>> {
    inline static BOOLEAN serialize(ISerializer *s, std::map<TKEY, TVALUE> const &m) {
        BOOLEAN ret = false;
        INT32 mapSize = m.size();
        // GOS_LOG_INFO("serial type: std::map, change to basic type.\n");
        ret = serialize_traveller<INT32>::serialize(s, mapSize);
        if (true == ret) {
            // for ( const auto& [key, value] : m) {
            for (auto it = m.begin(); it != m.end(); ++it) {
                BOOLEAN retKey = serialize_traveller<TKEY>::serialize(s, it->first);
                BOOLEAN retValue = serialize_traveller<TVALUE>::serialize(s, it->second);
                if (false == retKey || false == retValue) {
                    return false;
                }
            }
        }
        return ret;
    }
};

// std::unordered_map
// map size, map key, map key value
template <typename TKEY, typename TVALUE>
struct serialize_traveller<std::unordered_map<TKEY, TVALUE>> {
    inline static BOOLEAN serialize(ISerializer *s, std::unordered_map<TKEY, TVALUE> const &m) {
        BOOLEAN ret = false;
        INT32 mapSize = m.size();
        // GOS_LOG_INFO("serial type: std::unordered_map, change to basic type.\n");
        ret = serialize_traveller<INT32>::serialize(s, mapSize);
        if (true == ret) {
            for (auto it = m.begin(); it != m.end(); ++it) {
                BOOLEAN retKey = serialize_traveller<TKEY>::serialize(s, it->first);
                BOOLEAN retValue = serialize_traveller<TVALUE>::serialize(s, it->second);
                if (false == retKey || false == retValue) {
                    return false;
                }
            }
        }
        return ret;
    }
};

/**
 * @relates ISerializer
 * @brief deserialize values from a deserializer.
 *
 * This is a parameterized function for serialize multiple values in once call,
 * and it provides a universal method to support INT*,UINT*,FLOAT*,BOOLEAN,
 * objects which has implemented a `BOOLEAN serialize(ISerializer*)` method.
 * and containers(vector,array,map,etc) with type check in compile time.
 *
 * @param[in] s a serializer, which will only accessed during this call.
 * @param[out] args parameterized arguments to be serialized,
 * any caller pass a pointer to this function should hold the onwnership
 * of that pointer, this function only access it during the function call.
 * @return true if deserialization is succeed else false.
 */
template <typename... Args>
BOOLEAN serialize(dra::com::ISerializer* s, Args const&... args) {
    return serialize_traveller<Args...>::serialize(s, args...);
}

// length field serialization ------------------------------------------------
inline BOOLEAN serialize_lf(ISerializer *s, UINT32 sizeOfLengthField, UINT32 length) {
    BOOLEAN ret = true;
    switch (sizeOfLengthField) {
        case 1:
            ret = serialize(s, static_cast<UINT8>(length));
            break;
        case 2:
            ret = serialize(s, static_cast<UINT16>(length));
            break;
        case 4:
            ret = serialize(s, length);
            break;
        case 0:
        default:
            break;
    }
    return ret;
}

// C array serialization
template <typename T>
BOOLEAN serialize_carray(ISerializer *s, T* a, size_t size) {
    for (size_t i = 0; i < size; i++) {
        if (!serialize(s, a[i])) {
            return false;
        }
    }
    return true;
}

// }  // namespace someip
}  // namespace com
}  // namespace dra

#endif  // _DRA_COM_SOMEIP_SERIALIZER_H_
